package io.gitee.sunjx93.plugin.dataset.bean;


import com.alibaba.fastjson.JSONObject;
import io.gitee.sunjx93.plugin.common.constant.CollectionContants;
import io.gitee.sunjx93.plugin.common.exception.PluginException;
import io.gitee.sunjx93.plugin.dataset.bean.annotation.SheetParam;
import io.gitee.sunjx93.plugin.dataset.bean.annotation.SheetResult;
import io.gitee.sunjx93.plugin.dataset.bean.pojo.BeanDsResult;
import io.gitee.sunjx93.plugin.dataset.bean.pojo.BeanQuery;
import io.gitee.sunjx93.plugin.dataset.bean.util.FieldUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public class BeanDatasetDefinition {

    @Autowired
    private ApplicationContext applicationContext;

    public BeanDsResult queryData(BeanQuery query) throws InvocationTargetException, IllegalAccessException {
        BeanDsResult result = new BeanDsResult();
        if (!applicationContext.containsBean(query.getBeanName())) {
            result.setMsg("未找到Bean：" + query.getBeanName());
            return result;
        }
        Object bean = applicationContext.getBean(query.getBeanName());
        Class<?> beanClass = bean.getClass();
        if (AopUtils.isCglibProxy(bean)) {
            // cglib 类型抹除，取得super
            Type genericSuperclass = bean.getClass().getSuperclass();
            beanClass = (Class<?>) genericSuperclass;
        }
        Logger.getGlobal().info("[plugin]:bean=" + beanClass.getSimpleName());
        Method[] declaredMethods = beanClass.getDeclaredMethods();
        Method targetMethod = null;

        for (Method method : declaredMethods) {
            try {
                if (this.methodCheck(method, query, result)) {
                    targetMethod = method;
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (Objects.isNull(targetMethod)) {
            result.setMsg("未找到方法:" + query.getMethodName());
            return result;
        }
        Object o = this.req(targetMethod, query, bean);
        // limit
        Collection<?> req = (Collection<?>) this.fieldFinder(targetMethod, o);
        if (query.getLimit() > 0 && query.getLimit() < req.size()) {
            req = req.stream().limit(query.getLimit()).collect(Collectors.toList());
        }
        result.setData(req);
        return result;
    }

    /**
     * 判断是否是我要的目标函数，反射的话也过不了，是校验入参的
     * @param method
     * @param query
     * @param result
     * @return
     */
    private boolean methodCheck(Method method, BeanQuery query, BeanDsResult result) {
        Logger.getGlobal().info("[plugin]:method=" + method.getName());
        if (!query.getMethodName().equals(method.getName())) {
            return false;
        }
        SheetResult annotation = method.getAnnotation(SheetResult.class);

        Class<?> returnType = null;
        Type type = null;
        if (!Objects.isNull(annotation)) {
            Class<?> tempReturn = method.getReturnType();
            if (StringUtils.isNotBlank(annotation.resultName())) {
                for (String s : annotation.resultName().split("//.")) {
                    try {
                        Field field = tempReturn.getDeclaredField(s);
                        type = field.getGenericType();
                        tempReturn = field.getType();
                    } catch (NoSuchFieldException e) {
                        throw new PluginException(String.format("sheetBean 返回体定义错误:%s ,bean = %s,method = %s ", s, method.getClass().getName(), method.getName()));
                    }
                }
            }
            returnType = tempReturn;

        } else {
            returnType = method.getReturnType();
            type = method.getGenericReturnType();
        }

        if (!Iterable.class.isAssignableFrom(returnType)) {
            throw new PluginException("返回体必须为 " + Collection.class.getName() + "的实现类");
        }

        result.setStructs(this.structHandle(type));
        return true;
    }

    /**
     * 返回体获取结果
     *
     * @param method
     * @param result
     * @return
     */
    private Object fieldFinder(Method method, Object result) {
        SheetResult annotation = method.getAnnotation(SheetResult.class);
        if (Objects.isNull(annotation) || StringUtils.isBlank(annotation.resultName())) {
            return result;
        } else {
            Object temp = result;
            for (String s : annotation.resultName().split("//.")) {
                temp = FieldUtil.getPrivateValue(temp, s);
            }
            return temp;
        }
    }

    /**
     * 请求bean method
     */
    private Object req(Method method, BeanQuery query, Object bean) throws InvocationTargetException, IllegalAccessException {

        if (method.getParameterCount() == 1) {
            Parameter parameter = method.getParameters()[0];
            SheetParam annotation = parameter.getAnnotation(SheetParam.class);

            JSONObject jsonObject = StringUtils.isBlank(query.getParams()) ? new JSONObject() : JSONObject.parseObject(query.getParams());
            Object param = null;
            if (!Objects.isNull(annotation)) {
                // 有sheetparam ，直接转单参
                param = jsonObject.getObject(annotation.value(), parameter.getType());
            } else {
                // 无sheetparam 直接转java object
                param = jsonObject.toJavaObject(parameter.getType());
            }
            return method.invoke(bean, param);
        } else if (method.getParameterCount() == 0) {
            // 空
            return method.invoke(bean);
        } else {
            // param 1.@sheetparam ...  sheetparam 捻平的情况
            Parameter[] parameters = method.getParameters();
            JSONObject jsonObject = StringUtils.isBlank(query.getParams()) ? new JSONObject() : JSONObject.parseObject(query.getParams());
            Object[] param = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameterType = parameters[i];
                SheetParam annotation = parameterType.getDeclaredAnnotation(SheetParam.class);
                if (!Objects.isNull(annotation) && StringUtils.isNotBlank(annotation.value())) {
                    String annoValue = annotation.value();
                    if (this.basicTypeCheck(parameterType.getType())) {
                        param[i] = jsonObject.getObject(annoValue, parameterType.getType());
                    } else {
                        Object o = jsonObject.getJSONObject(annoValue).toJavaObject(parameterType.getType());
                        param[i] = o;
                    }

                }
            }
            return method.invoke(bean, param);
        }
    }

    /**
     * 返回item 结构体
     * 返回体为List<?>
     * 或 包含list 属性的T，用  SheetBean 注解修饰
     * 如果是非基本类型，那么就跳过该字段
     *
     * @return bean ds item structure
     */
    private List<BeanDsResult.ResultStruct> structHandle(Type genericReturnType) {
        List<BeanDsResult.ResultStruct> list = new ArrayList<>();
        Type actualTypeArgument = null;
        if (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            actualTypeArgument = actualTypeArguments[0];
        }

        Field[] fields = ((Class<?>) actualTypeArgument).getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            BeanDsResult.ResultStruct rs = new BeanDsResult.ResultStruct();
            Field f = fields[i];
            String type = this.typeTransfer(f.getType());
            if (Objects.isNull(type) || Modifier.isStatic(f.getModifiers())) {
                continue;
            }

            rs.setfName(f.getName());
            rs.setfType(type);
            rs.setfClassName(f.getType().getSimpleName());
            rs.setSort(i);
            list.add(rs);
        }
        return list;
    }


    /**
     * 数据类型转换
     *
     * @param t
     * @return
     */
    private String typeTransfer(Class<?> t) {
        switch (t.getSimpleName()) {
            case "Integer":
            case "Short":
            case "Long":
            case "BigDecimal":
            case "Double":
            case "Float":
            case "int":
            case "short":
            case "long":
            case "double":
            case "float":
                return CollectionContants.BDP_DATA_TYPE_NUMBER_STR;
            case "String":
                return CollectionContants.BDP_DATA_TYPE_TEXT_STR;
            case "Date":
                return CollectionContants.BDP_DATA_TYPE_DATETIME_STR;
            default:
                Logger.getGlobal().warning("无法解析的数据类型：" + t.getSimpleName());
                return null;
        }
    }


    /**
     * 基本数据类型辨认
     */
    private boolean basicTypeCheck(Class<?> t) {
        switch (t.getSimpleName()) {
            case "Integer":
            case "Short":
            case "Long":
            case "BigDecimal":
            case "Double":
            case "Float":
            case "int":
            case "short":
            case "long":
            case "double":
            case "float":
            case "String":
            case "Date":
                return true;
        }
        return false;
    }

}
